/*
 * modsAX Javascript Library
 * modsAX.com 2009-2010 squall
 * modsAX are licensed under http://www.modsax.com/license/
 */

var modsGrid = Class.create(modsAX, {
  version       : "modsGrid V1.0",
  author        : "SQUALL",
  createDate    : "2010.04.29",
  lastModifyDate: "2010.04.29",
  initialize: function($super){
    $super();
    this.ds = [];
    this.moveSens = 0;
    this.sorter = {};
    this.basket = [];
    this.selects = [];
    this.page = {};
    with(this){
      config.ids = [0];
      config.containerCss = "modsGrid";
      config.scrollerCss = "scroller";
      config.headCss = "head";
      config.bodyCss = "body";
      config.footCss = "foot";
      config.maskCss = "mask";
      config.headACss = "headA";
      config.descCss = "sortDesc";
      config.ascCss = "sortAsc";
      config.resizerCss = ["resizer", "resizer_active"];
      config.pageCss  = "page";
      config.selectClassName   = "readySelect";
      config.beselectClassName = "beSelected";
      config.foot = false;
      config.height = 100;
      config.topMargin = 20;
      config.bodyWidth = 0;
      config.botMargin = 0;
      config.moveSens = 5;
      config.ie67adj = -2;
    }
  },
  init: function(){
    this._self    = jQuery("#"+this.config.rootElementID);
    var config = this.config;
    // po
    var po = [];
    po.push("<div id=\""+config.rootElementID+"_container\" class=\""+config.containerCss+"\">");
    po.push(" <div id=\""+config.rootElementID+"_head\"     class=\""+config.headCss+"\"></div>");
    po.push(" <div id=\""+config.rootElementID+"_scroller\" class=\""+config.scrollerCss+"\">");
    po.push("   <div id=\""+config.rootElementID+"_body\"   class=\""+config.bodyCss+"\"></div>");
    po.push(" </div>");
    if(config.foot) po.push(" <div id=\""+config.rootElementID+"_foot\"   class=\""+config.footCss+"\"></div>");
    po.push("</div>");
    this._self.append(po.join(''));
    // define element
    this.container= jQuery("#"+config.rootElementID+"_container");
    this.scroller = jQuery("#"+config.rootElementID+"_scroller");
    this.head     = jQuery("#"+config.rootElementID+"_head");
    this.body     = jQuery("#"+config.rootElementID+"_body");
    if(config.foot) this.foot  = jQuery("#"+config.rootElementID+"_foot");
    //init head
    this.initHead();
    //init foot
    if(config.foot) this.initFoot();
    //element set size
    this.resetSize();
    //bind head event
    jQuery("#"+config.rootElementID+"_head ."+this.config.headACss).bind("click", this.onsort.bind(this));
    this.body.bind("click", this.onbodyClick.bind(this));
    this.scroller.bind("click", this.onScrollerClick.bind(this));
    this.container.bind("keydown", this.onbodyKeydown.bind(this));

    this.dragStart = this.ondragStart.bind(this);
    this.container.bind("dragstart", this.dragStart);
    jQuery(window).bind("resize", this.onDocResize.bind(this));
  },
  initHead: function(){
    var re = this.config.rootElementID;
    var colGroup = this.config.colGroup;
    var head = this.config.head;
    var colMap = new Array(head.length);
    for(var a=0;a<colMap.length;a++) colMap[a] = new Array(colGroup.length);

    function saveCellPosition(rp, cp, i, k){
      var rid = "";
      for(var a=i;a<(i+rp);a++){
        if(myb != undefined) k = myb; // redefine col posl because we find next cell.
        for(var b=k;b<(k+cp);b++){
          try{
          if(colMap[a][b] == undefined){ // normal
            colMap[a][b] = a+"_"+b; rid = a+"_"+b;
          }else{ // already have value find next cell;
            var myb = b; while(true){
            if(colMap[a][myb] == undefined){
              colMap[a][myb] = a+"_"+myb; rid = a+"_"+myb;
              break; }
            myb++;}
          }
          }catch(e){

          }
        }
      }
      return rid;
    }

    var ie67adj = this.config.ie67adj;
    function getMargin(){
      var br = mods.browser.browser;
      var ver = mods.browser.version;
      if(br == "ie" && ver < 8){
        return ie67adj;
      }else{
        return 0;
      }
    }

    var po = [];
    po.push("<table cellpadding='0' cellspacing='0'>");
    po.push("<colgroup>");
    jQuery.each(colGroup, function(i, n){
    po.push("<col id=\""+( re + "_cg_" + i )+"\""
            + ( (n.width != undefined) ? " width='"+(n.width + getMargin())+"'" : "" )
            +"/>");
    });
    po.push("<col />");
    po.push("</colgroup>");
    po.push("<thead>");

    var lk = 0;
    for(var i = 0;i < head.length;i++){
    po.push(" <tr>");
      for(var k = 0;k < head[i].length;k++){
        var n = head[i][k];
        var rs = n.rowspan || 1, cs = n.colspan || 1;
        var seq = saveCellPosition(rs, cs, i, k);
    po.push("   <td id=\""+(re+"_"+seq)+"\" "
            + ( (cs > 1) ? " colspan='"+n.colspan+"'" : "" )
            + ( (rs > 1) ? " rowspan='"+n.rowspan+"'" : "" )
            + ( (n.className != undefined) ? " class='"+n.className+"'" : "" )
            + ( (n.style != undefined) ? " style='"+n.style+"'" : "") + ">"+n.nm +"</td>");
      }
      if(i == 0){
    po.push("   <td class=\"null\" rowspan=\""+(head.length)+"\">&nbsp;</td>");
      }
    po.push(" </tr>");
    }
    po.push("</thead>");
    po.push("</table>");
    this.head.html(po.join(''));
    this.colMap = colMap;
  },
  initFoot: function(){
    var re = this.config.rootElementID;
    if(this.config.foot.page){
      this.page = this.config.foot.page;
      var po = [];
      po.push("<div class=\""+this.config.pageCss+"\">");
      po.push(" <a href=\"#pageException\" id=\""+re+"_prevPage\" class=\"mdp_prev\">Prev</a>");
      po.push(" <div>");
      po.push(" <select id=\""+re+"_absPage\"><option value=\"1\">1</option></select>");
      po.push(" / <a id=\""+re+"_pgCount\">00<a>");
      po.push(" </div>");
      po.push(" <a href=\"#pageException\" id=\""+re+"_nextPage\" class=\"mdp_next\">Next</a>");
      po.push(" <div id=\""+re+"_total\"></div>");
      po.push("</div>");
      this.foot.append(po.join(''));

      jQuery("#"+re+"_absPage").bind("change", this.onpageChange.bind(this));
      jQuery("#"+re+"_prevPage, #"+re+"_nextPage").bind("click", this.onpageChange.bind(this));
    }
  },
  onpageChange: function(event){
    if(event.target.tagName == "SELECT"){
      this.page.absPage = event.target.value;
      this.reLoad();
    }
    if(event.target.tagName == "A"){
      var re = this.config.rootElementID;
      var dirc = event.target.id.split("_").last();
      var absPG = jQuery("#"+re+"_absPage").val().number();
      if(dirc == "prevPage"){
        var newPG = jQuery("#"+re+"_absPage").val(absPG-1);
        this.page.absPage = newPG.val().number();
      }else{
        var newPG = jQuery("#"+re+"_absPage").val(absPG+1);
        this.page.absPage = newPG.val().number();
      }
      if(absPG != this.page.absPage) this.reLoad();
    }
  },
  resetSize: function(){
    var re = this.config.rootElementID;
    var colGroup = this.config.colGroup;
    var _self = this._self;
    var _selfBox = {width:_self.width(), height:_self.height()||this.config.height};
    this._self.data(this._self.offset());
    this.scroller.css(_selfBox);

    this.config.bodyWidth     = this.body.width();
    this.config.topMargin     = this.head.outerHeight();
    this.config.botMargin     = (this.config.foot) ? this.foot.outerHeight() : 0;

    this.head.width(this.config.bodyWidth);
    if(this.config.foot) this.foot.width(this.config.bodyWidth);
    this.body.css({"margin-top":this.config.topMargin, "margin-bottom":this.config.botMargin});
    if(mods.browser.browser == "ie" || mods.browser.browser == "op") this.body.css({"padding-bottom":this.config.botMargin});

    if(this.config.foot)  _selfBox.height -= this.config.botMargin;

    //colGroup head define
    var k = this.colMap.length - 1;
    var onerr = false;
    for(var i=0;i<this.colMap[0].length;i++){
      var n = this.colMap[k][i];
      if(n == undefined){onerr = true; break;}

      var endTD = jQuery("#"+re+"_"+n);
      if(!colGroup[i].width) colGroup[i].width = endTD.width();

      endTD.html("<a class=\""+this.config.headACss+"\" id=\""+re+"_ha_"+n+"\" href=\"#modsExceptionScript\" style=\"line-height:"+(endTD.height())+"px;\">"
      +"<div class=\"sorter\"></div>"
      +endTD.html()+"</a>");
    }

    //resizer init
    var sx = 0;
    for(var k=0;k<colGroup.length;k++){
      this.config.colGroup[k].left = sx;
      sx += colGroup[k].width;
      var resizer = jQuery("<div id=\""+re+"_r_"+k+"\" class=\""+this.config.resizerCss[0] + ( (mods.browser.browser == "ie") ? " modsBlankBGforIE" : "" ) +"\" style=\"height:"+this.head.height()+"px;\"></div>");
      this.container.append(resizer);
      resizer.css({left:sx-resizer.width()/2});
    }

    if(onerr) this.head.html("<table cellpadding='0' cellspacing='0'><thead><tr><td>head 구성에 실패! {colGroup갯수와 head갯수가 맞지 않습니다.}</td></thead></table>");

    this.container.find(".resizer").bind("mousedown", this.onresizedown.bind(this));
  },
  onDocResize: function(){
    var re = this.config.rootElementID;
    var colGroup = this.config.colGroup;
    var _self = this._self;
    var _selfBox = {width:_self.width(), height:_self.height()||this.config.height};
    this._self.data(this._self.offset());
    this.scroller.css(_selfBox);

    this.config.bodyWidth     = this.body.width();
    this.config.topMargin     = this.head.height();
    this.config.botMargin     = (this.config.foot) ? this.foot.height() : 0;

    this.head.width(this.config.bodyWidth);
    if(this.config.foot) this.foot.width(this.config.bodyWidth);
    this.body.css({"margin-top":this.config.topMargin, "margin-bottom":this.config.botMargin});
    if(mods.browser.browser == "ie" || mods.browser.browser == "op") this.body.css({"padding-bottom":this.config.botMargin});

    if(this.config.foot)  _selfBox.height -= this.config.botMargin;
  },
  load: function(objs){
    if(this.config.dataType == "script"){ //스크립트 지정 방식
      this.replaceData(objs);
    }else{
      this.printMsg("Loading..");
      this.page.absPage = 1;
      this._setConfigLoad(objs); // config loader
      var url   = this._getUrl("load");
      var pars  = this._getPars();

      var onreq = this.onLoad.bind(this);
      if(url == "") this.outMsg("You will set the Load URL");
      new modsreq(url, {pars:pars, onsucc:onreq});
    }
  },
  reLoad: function(){
    this.printMsg("Loading..");
    var url   = this._getUrl("load");
    var pars  = this._getPars();
    var onreq = this.onLoad.bind(this);
    if(url == "") this.outMsg("You will set the Load URL");
    new modsreq(url, {pars:pars, onsucc:onreq});
  },
  rePrint: function(){
    this.replaceData(this.ds);
  },
  onLoad: function(res){
    if(res.result == "ok"){
      this.replaceData(res.ds);
      if(res.rs){
        this.rs = res.rs;
        if(this.sorter.a) this.sort();
        else              this.printRs();
      }
    }else{
      this.printMsg(res.msg.dec() + "<br/>" + res.body);
    }
  },
  replaceData: function(obj){
    this.ds = obj;
    this.body.empty();
    this.printData(obj);
  },
  printData: function(o){
    var config = this.config;
    var re = config.rootElementID;
    var c = config.colGroup;
    var ids = this.config.ids;

    var po = [];
    for(var i=0;i<o.length;i++){

      var obj = this.basket.get(function(n){
        var isSame = 0;
        for(var a=0;a<ids.length;a++){
          if(n[ids[a]] == o[i][ids[a]]){
            isSame++;
          }
        }
        return (isSame == ids.length);
      });
      var dlClass = this.config.selectClassName;
      if(obj){
        o[i] = obj;
        dlClass += " edit";
      }

    // loop start
        po.push("<dl href=\"#modsExceptionScript\" id=\""+re+"_d_"+i+"\" class=\""+dlClass+"\">");
        po.push("<dt><a name=\""+re+"_d_"+i+"\" class=\"no\" href=\"#"+re+"_d_"+i+"\">"+(i+1)+"번째</a></dt>");
      for(var k=0;k<c.length;k++){
        var di = c[k].di;
        var adj = 4;
        if(k == 0 || k == c.length) adj = 5;
        po.push("<dd class=\"modsgrid_cell_"+k+"\" id=\""+re+"_d_"+i+"D"+k+"\" style=\"width:"+(c[k].width-adj)+"px;"
               + ( (c[k].align != undefined)? "text-align:"+c[k].align+";" : "" ) +"\">");

        if(c[k].convert) po.push( mods.ctype( o[i][di], c[k].convert ) );
        else             po.push( mods.ctype( o[i][di], c[k].ty ) );
        po.push("</dd>");
      }
        po.push("</dl>");
    // loop end
    }
    po.push("<div class=\"line\"></div>");
    this.body.append(po.join(''));
  },
  printMsg: function(msg){
    this.body.empty();
    var po = [];
    po.push("<div class=\"error\">");
    po.push( msg );
    po.push("</div>");
    this.body.append(po.join(''));
  },
  printRs: function(){
    var re = this.config.rootElementID;
    if(this.config.foot.page){
      this.page.pgCount = this.rs.pgCount.number();
      this.page.total   = this.rs.total.number();
      this.page.absPage = this.rs.absPage.number();
      this.page.total   = this.rs.total.number();

      var selectObj = jQuery("#"+re+"_absPage")[0];
      for(var a=selectObj.length;a>-1;a--){
        selectObj.options[a] = null;
      }
      for(var a=1;a<this.page.pgCount+1;a++){
        var addNo = selectObj.length;
        optionS = new Option(a, a);
        selectObj.options[addNo] = optionS;
        if(a == this.page.absPage) selectObj.options[addNo].selected = true;
      }
      jQuery("#"+re+"_pgCount").html(this.page.pgCount);
      jQuery("#"+re+"_total").html("<b>"+this.page.total+"</b> Count");
    }
  },
  /************** event ****************/
  onScrollerClick: function(event){
    if(event.target.id == this.config.rootElementID+"_scroller"){
      this.clearSelects();
      return;
    }
  },
  onbodyClick: function(event){
    if(event.target.tagName != "DD" && event.target.tagName != "DL"){
      return;
    }
    var ei = event.target.id.split("_").last().split("D");
    var r = ei[0], c = ei[1];
    this.cellEdit(r, c);

    var selectElement = event.target;
    if(event.target.tagName == "DD"){
      selectElement = jQuery(selectElement).parent().get(0);
    }
    if(jQuery(selectElement).hasClass(this.config.selectClassName)){
      if(selectElement){
        if(event.shiftKey){
          this.shiftSelects(selectElement);
        }else if(event.ctrlKey){
          this.toggleSelects(selectElement);
        }else{
          this.clickSelects(selectElement);
        }
      }
      jQuery(selectElement).find("dt a").focus();
    }else{
      this.clearSelects();
      return;
    }
  },
  onbodyKeydown: function(event){
    if(this.selects.length == 0) return;
    var myselect = this.selects.last();
    if(event.keyCode == mods.Event.KEY_UP){
      var ne = jQuery(myselect).prev("dl").get(0);
      if(!ne) ne = jQuery(myselect).parent().find("dl").last().get(0);
      this.clickSelects(ne);
      event.stopPropagation(); // disable  event
      return false;
    }else if(event.keyCode == mods.Event.KEY_DOWN){
      var ne = jQuery(myselect).next("dl").get(0);
      if(!ne) ne = jQuery(myselect).parent().find("dl").first().get(0);
      this.clickSelects(ne);
      event.stopPropagation(); // disable  event
      return false;
    }else if(event.keyCode == mods.Event.KEY_LEFT){
      var ne = jQuery(myselect).prev("dl").get(0);
      if(!ne) ne = jQuery(myselect).parent().find("dl").last().get(0);
      this.clickSelects(ne);
      event.stopPropagation(); // disable  event
      return false;
    }else if(event.keyCode == mods.Event.KEY_RIGHT){
      var ne = jQuery(myselect).next("dl").get(0);
      if(!ne) ne = jQuery(myselect).parent().find("dl").first().get(0);
      this.clickSelects(ne);
      event.stopPropagation(); // disable  event
      return false;
    }else if(event.keyCode == mods.Event.KEY_ESC){
      this.clearSelects();
    }else if(event.keyCode == mods.Event.KEY_RETURN){
      this.clickSelects(myselect);
    }
  },
  onresizedown: function(event){
    this.resizeTarget = jQuery(event.target);
    this.resizeTarget.addClass(this.config.resizerCss[1]);

    this.mouseup = this.onresizeup.bind(this);
    jQuery(document).bind("mouseup", this.mouseup);

    this.mousemove = this.onresizemove.bind(this);
    jQuery(document).bind("mousemove", this.mousemove);

    this.selectstart = this.onselectStart.bind(this);
    jQuery(document).bind("selectstart", this.selectstart);

    this.container.addClass("modsSelectNone");
  },
  ondragStart: function(event){
    event.stopPropagation(); // disable  event
    return false;
  },
  onselectStart: function(event){
    event.stopPropagation();
    return false;
  },
  onresizeup: function(event){
    this.resizeTarget.removeClass(this.config.resizerCss[1]);
    jQuery(document).unbind("mouseup", this.mouseup);
    jQuery(document).unbind("mousemove", this.mousemove);
    jQuery(document).unbind("selectstart", this.selectstart);
    this.container.removeClass("modsSelectNone");
    // resize target
    this.resizeColumn();
    this.resizeTarget = null;
  },
  onresizemove: function(event){
    if(!event.pageX) return;
    this.mouse = {x:event.pageX||0, y:event.pageY||0}; //Event.pointer(event);
    //드래그 감도 적용
    if(this.config.moveSens > this.moveSens) this.moveSens++;
    if(this.moveSens == this.config.moveSens) this.resizerMove();
  },
  resizerMove: function(){
    this.resizeTarget.css({"left":( this.mouse.x - this._self.data("left") - 1 )+"px"});
  },
  onsort: function(event){
    if(event.target.tagName != "A") return;
    var seq = event.target.id.split("_").last();
    var a = event.target;

    if(this.sorter.a){
      if(this.sorter.a == a){
        if(this.sorter.sort == "desc"){
          jQuery("#"+a.id+" .sorter").removeClass(this.config.descCss);
          jQuery("#"+a.id+" .sorter").addClass(this.config.ascCss);
          this.sorter.sort = "asc";
        }else{
          jQuery("#"+a.id+" .sorter").removeClass(this.config.ascCss);
          jQuery("#"+a.id+" .sorter").addClass(this.config.descCss);
          this.sorter.sort = "desc";
        }
      }else{
        jQuery("#"+this.sorter.a.id+" .sorter").removeClass(this.config.descCss+" "+this.config.ascCss);
        jQuery("#"+a.id+" .sorter").addClass(this.config.descCss);
        this.sorter.a = a;
        this.sorter.seq = seq;
        this.sorter.sort = "desc";
      }
    }else{
      jQuery("#"+a.id+" .sorter").addClass(this.config.descCss);
      this.sorter.a = a;
      this.sorter.seq = seq;
      this.sorter.sort = "desc";
    }
    this.sort();
  },
  sort: function(){
    var seq = this.sorter.seq;
    var ty = this.config.colGroup[seq].ty.toLowerCase();
    var di = this.config.colGroup[seq].di;
    if(this.sorter.sort == "desc"){
      var myds = this.ds.sort(function(a, b){
        var v1 = mods.sortType(a[di], ty);
        var v2 = mods.sortType(b[di], ty);
        if(v1 < v2)       return 1;
        else if(v1 > v2)  return -1;
        else if(v1 == v2) return 0;
      });
    }else{
      var myds = this.ds.sort(function(a, b){
        var v1 = mods.sortType(a[di], ty);
        var v2 = mods.sortType(b[di], ty);
        if(v1 < v2)       return -1;
        else if(v1 > v2)  return 1;
        else if(v1 == v2) return 0;
      });
    }
    this.clearSelects();
    this.replaceData(myds);
  },
  /************** style method ****************/
  resizeColumn: function(){
    var ie67adj = this.config.ie67adj;
    function getMargin(){
      var br = mods.browser.browser;
      var ver = mods.browser.version;
      if(br == "ie" && ver < 8){
        return ie67adj;
      }else{
        return 0;
      }
    }

    var br = mods.browser.browser;
    var ver = mods.browser.version;

    var minw = 30;
    var rpos = this.resizeTarget.position();
    var rw = this.resizeTarget.width() / 2;
    var i = this.resizeTarget[0].id.split("_").last().number();
    var config = this.config;
    var re = config.rootElementID;
    var c = config.colGroup;
    var mywidth = (rpos.left - c[i].left) + rw;
    if(mywidth < minw) mywidth = minw;
    this.resizeTarget.css({"left": ( c[i].left + mywidth - rw ) });

    var adj = 4;
    if(i == 0 || i == c.length) adj = 5;
    jQuery("#"+re + "_cg_" + i).css({"width":(mywidth+getMargin())});
    c[i].width = mywidth;
    jQuery("#"+re+"_body .modsgrid_cell_"+i).css({"width":(mywidth-adj)});

    //repos resizer
    var sx = c[i].left + mywidth;
    var tw = 0;
    for(var k=0;k<c.length;k++){
      tw += c[k].width;
      if(k > i){
        this.config.colGroup[k].left = sx;
        sx += c[k].width;
        jQuery("#"+re+"_r_"+k).css({"left":(sx-rw)});
      }
    }

    function setCellWidth(k){
      var adj = 4;
      if(k == 0 || k == c.length-1) adj = 5;
      jQuery("#"+re+"_body .modsgrid_cell_"+k).css({"width":(c[k].width-adj)});
      jQuery("#"+re + "_cg_" + k).css({"width":c[k].width+getMargin()});
    }

    if(tw > this.config.bodyWidth){ // width redefine;
      var minusWidth = tw - this.config.bodyWidth;

      for(var k=(c.length-1);k>-1;k--){
        if(k != i){
          var newWidth = c[k].width - minusWidth;
          if(newWidth > minw){
            minusWidth = 0;
            c[k].width = newWidth;
            setCellWidth(k);
            break;
          }else{
            minusWidth -= (c[k].width-minw);
            c[k].width = minw;
            setCellWidth(k);
          }
        }
      }
      if(minusWidth > 0){
        c[i].width = this.config.bodyWidth - (c.length * minw);
        setCellWidth(i);
      }

      this.reposResizer();
    }
  },
  reposResizer: function(){
    var config = this.config;
    var re = config.rootElementID;
    var c = config.colGroup;
    var rw = jQuery("#"+re+"_r_0").width() / 2;
    var sx = 0;
    for(var k=0;k<c.length;k++){
      this.config.colGroup[k].left = sx;
      sx += c[k].width;
      jQuery("#"+re+"_r_"+k).css({"left":(sx-rw)});
    }
  },
  /************** cellEdit method ****************/
  cellEdit: function(r, c){
    var re = this.config.rootElementID;
    var cg = this.config.colGroup;
    if(r < 0) r = this.ds.length-1;
    if(r > this.ds.length-1) r = 0;
    if(c < 0 || c > cg.length-1) return;
    if(!cg[c]) return;
    if(cg[c].edit){
      var adj = 8;
      if(c == 0 || c == cg.length) adj = 9;
      var edit = jQuery("<input type=\"text\" class=\"modsEdit\" style=\"width:"+(cg[c].width-adj)+"px;\" id=\""+re+"_d_"+r+"D"+c+"_edit\" value=\""+this.ds[r][cg[c].di]+"\" />");
      jQuery("#"+re+"_d_"+r+"D"+c).html(edit);
      setTimeout(function(){
        edit.select();
      }, 100);
      edit.bind("blur", this.oncellEdit.bind(this));
      edit.bind("keyup", this.oncellEdit.bind(this));
    }
  },
  oncellEdit: function(event){
    if(event.target.tagName != "INPUT") return;
    var id = event.target.id;
    var did = id.replace("_edit", "");
    var ei = did.split("_").last().split("D");
    var r = ei[0], c = ei[1];
    if(event.type == "blur"){
      this.cellChange("save", id);
    }else{
      if(event.keyCode == mods.Event.KEY_ESC){
        this.cellChange("cancel", id);
        this.clearSelects();
      }else if(event.keyCode == mods.Event.KEY_RETURN){
        this.cellChange("save", id);
        this.clearSelects();
      }else if(event.keyCode == mods.Event.KEY_UP){
        this.cellChange("save", id);
        this.cellEdit(r-1, c);
      }else if(event.keyCode == mods.Event.KEY_DOWN){
        this.cellChange("save", id);
        this.cellEdit((r.number()+1), c);
      }else if(event.keyCode == mods.Event.KEY_LEFT){
        this.cellChange("save", id);
        this.cellEdit(r, c.number()-1);
      }else if(event.keyCode == mods.Event.KEY_RIGHT){
        this.cellChange("save", id);
        this.cellEdit(r, c.number()+1);
      }
    }
  },
  cellChange: function(act, id){
    var re = this.config.rootElementID;
    var did = id.replace("_edit", "");
    var ei = did.split("_").last().split("D");
    var r = ei[0], c = ei[1];
    var cg = this.config.colGroup;

    jQuery("#"+id).unbind();
    if(act == "save"){
      if(this.ds[r][cg[c].di] != mods.ctype(jQuery("#"+id).val(), cg[c].edit) ){
        this.ds[r][cg[c].di] = mods.ctype(jQuery("#"+id).val(), cg[c].edit);
        jQuery("#"+re+"_d_"+r).addClass("edit");
        var ids = this.config.ids;

        var adder = this.ds[r];
        var result = this.basket.not(function(n){
          var isSame = 0;
          for(var a=0;a<ids.length;a++){
            if(n[ids[a]] == adder[ids[a]]){
              isSame++;
            }
          }
          return (isSame == ids.length);
        });

        if(result){
          this.basket.push(adder);
        }else{
          var obj = this.basket.getObj(function(n){
            var isSame = 0;
            for(var a=0;a<ids.length;a++){
              if(n[ids[a]] == adder[ids[a]]){
                isSame++;
              }
            }
            return (isSame == ids.length);
          });
          this.basket[obj.index] = this.ds[r];
        }
      }
      jQuery("#"+did).html(mods.ctype(jQuery("#"+id).val(), cg[c].edit));
      if(this.config.onBasket) this.config.onBasket(this.basket);
    }else if(act == "cancel"){
      jQuery("#"+did).html(this.ds[r][cg[c].di]);
    }
  },
  getBasket: function(){
    return this.basket;
  },
  resetBasket: function(){
    this.basket.clear();
    this.reLoad();
    if(this.config.onBasket) this.config.onBasket(this.basket);
  },
  /************** selector method ****************/
  clearSelects: function(){
    var beselectClassName = this.config.beselectClassName;
    jQuery.each(this.selects, function(i, n){
      jQuery(n).removeClass(beselectClassName);
    });
    this.selects.clear();
  },
  pushSelects: function(Obj){
    var hasSelect = this.selects.or(function(n){
      return n == Obj;
    });
    if(!hasSelect) this.selects.push(Obj);
  },
  clickSelects: function(Obj){
    var hasSelect = this.selects.or(function(n){
      return n == Obj;
    });
    if(!hasSelect) this.clearSelects();
    var beselectClassName = this.config.beselectClassName;
    jQuery(Obj).addClass(beselectClassName);
    this.pushSelects(Obj);

    jQuery(Obj).scrollToDiv({top:this.config.topMargin, bottom:this.config.botMargin});

    if(this.onclickobserver) clearTimeout(this.onclickobserver);
    var onclickSelects = this.onclickSelects.bind(this);
    this.onclickobserver = setTimeout(function(){
      onclickSelects(Obj);
    }, 300);
  },
  onclickSelects: function(Obj){
    var ei = Obj.id.split("_").last().split("D");
    var r = ei[0], c = ei[1];
    if(this.config.onclick) this.config.onclick(this.ds[r]);
  },
  toggleSelects: function(Obj){
    var beselectClassName = this.config.beselectClassName;
    var hasSelect = this.selects.or(function(n){
      return n == Obj;
    });
    if(hasSelect){
      jQuery(Obj).removeClass(beselectClassName);
      var collects = [];
      collects = jQuery.grep(this.selects, function(n, i){
        return (Obj != n);
      });
      this.selects = collects;
    }else{
      jQuery(Obj).addClass(beselectClassName);
      this.pushSelects(Obj);
    }
  },
  shiftSelects: function(Obj){
    var beselectClassName = this.config.beselectClassName;
    var clickSelects = this.clickSelects.bind(this);
    if(this.selects.length == 0){
      this.clickSelects(Obj);
    }else{
      var lastObj = this.selects.last();
      var si, ei;
      this.body.find("DL").each(function(i, n){
        if(n == Obj) ei = i;
        if(n == lastObj) si = i;
      });
      if(si == ei){
        this.toggleSelects(Obj);
      }else if(si < ei){
        for(var a=si;a<ei+1;a++){
          this.body.find("DL").eq(a).addClass(beselectClassName);
          this.pushSelects(this.body.find("DL").eq(a)[0]);
        }
      }else{
        for(var a=ei;a<si+1;a++){
          this.body.find("DL").eq(a).addClass(beselectClassName);
          this.pushSelects(this.body.find("DL").eq(a)[0]);
        }
      }
    }
  },
  getSelects: function(){
    return this.selects;
  },
  getSelectRs: function(){
    var s = this.selects;
    var r = [];
    for(var a=0;a<s.length;a++){
      var ei = s[a].id.split("_").last().split("D");
      r.push(this.ds[ei]);
    }
    return r;
  },
  /************** grid update method ****************/
  updateGrid: function(updateObj){
    var re = this.config.rootElementID;
    var ids = this.config.ids;
    var c = this.config.colGroup;
    
    var obj = this.ds.getObj(function(n){
      var isSame = 0;
      for(var a=0;a<ids.length;a++){
        if(n[ids[a]] == updateObj[ids[a]]){
          isSame++;
        }
      }
      return (isSame == ids.length);
    });
    this.ds[obj.index] = updateObj;
    var o = this.ds;
    var i = obj.index;
    for(var k=0;k<c.length;k++){
      var val = ""
      var di = c[k].di;
      if(c[k].convert) val = mods.ctype( o[i][di], c[k].convert );
      else             val = mods.ctype( o[i][di], c[k].ty );
      jQuery("#"+re+"_d_"+i+"D"+k).html(val);
    }    
  },
  deleteGrid: function(deleteObj){

  }
});